// ============================================================================
// 虚拟化列表组件
// ============================================================================

import React, { useState, useEffect, useRef, useMemo, useCallback } from 'react'
import { LoadingSpinner } from './loading-spinner'

export interface VirtualListProps<T = any> {
  items: T[]
  itemHeight: number | ((index: number, item: T) => number)
  renderItem: (item: T, index: number) => React.ReactNode
  height: number
  width?: number | string
  overscan?: number
  onScroll?: (scrollTop: number) => void
  onEndReached?: (() => void) | undefined
  endReachedThreshold?: number
  loading?: boolean
  loadingComponent?: React.ReactNode
  emptyComponent?: React.ReactNode
  className?: string
  itemKey?: keyof T | ((item: T, index: number) => string | number)
}

/**
 * 虚拟化列表组件
 * 只渲染可见区域的项目，提高大列表性能
 */
export function VirtualList<T = any>({
  items,
  itemHeight,
  renderItem,
  height,
  width = '100%',
  overscan = 5,
  onScroll,
  onEndReached,
  endReachedThreshold = 200,
  loading = false,
  loadingComponent,
  emptyComponent,
  className = '',
  itemKey
}: VirtualListProps<T>) {
  const containerRef = useRef<HTMLDivElement>(null)
  const [scrollTop, setScrollTop] = useState(0)
  const [isScrolling, setIsScrolling] = useState(false)
  const scrollTimeoutRef = useRef<NodeJS.Timeout>()

  // 计算项目高度
  const getItemHeight = useCallback((index: number, item: T): number => {
    return typeof itemHeight === 'function' ? itemHeight(index, item) : itemHeight
  }, [itemHeight])

  // 计算总高度和项目位置
  const { totalHeight, itemPositions } = useMemo(() => {
    let totalHeight = 0
    const positions: number[] = []

    for (let i = 0; i < items.length; i++) {
      positions[i] = totalHeight
      totalHeight += getItemHeight(i, items[i])
    }

    return { totalHeight, itemPositions: positions }
  }, [items, getItemHeight])

  // 计算可见范围
  const visibleRange = useMemo(() => {
    if (items.length === 0) {
      return { start: 0, end: 0 }
    }

    let start = 0
    let end = items.length - 1

    // 找到第一个可见项目
    for (let i = 0; i < items.length; i++) {
      if (itemPositions[i] + getItemHeight(i, items[i]) > scrollTop) {
        start = Math.max(0, i - overscan)
        break
      }
    }

    // 找到最后一个可见项目
    for (let i = start; i < items.length; i++) {
      if (itemPositions[i] > scrollTop + height) {
        end = Math.min(items.length - 1, i + overscan)
        break
      }
    }

    return { start, end }
  }, [items.length, itemPositions, scrollTop, height, overscan, getItemHeight])

  // 生成项目键
  const getItemKey = useCallback((item: T, index: number): string | number => {
    if (typeof itemKey === 'function') {
      return itemKey(item, index)
    }
    if (typeof itemKey === 'string') {
      return (item as any)[itemKey]
    }
    return index
  }, [itemKey])

  // 处理滚动
  const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
    const scrollTop = e.currentTarget.scrollTop
    setScrollTop(scrollTop)
    setIsScrolling(true)

    // 清除之前的超时
    if (scrollTimeoutRef.current) {
      clearTimeout(scrollTimeoutRef.current)
    }

    // 设置新的超时来检测滚动结束
    scrollTimeoutRef.current = setTimeout(() => {
      setIsScrolling(false)
    }, 150)

    onScroll?.(scrollTop)

    // 检查是否到达底部
    if (onEndReached) {
      const { scrollHeight, clientHeight } = e.currentTarget
      if (scrollHeight - scrollTop - clientHeight <= endReachedThreshold) {
        onEndReached()
      }
    }
  }, [onScroll, onEndReached, endReachedThreshold])

  // 清理超时
  useEffect(() => {
    return () => {
      if (scrollTimeoutRef.current) {
        clearTimeout(scrollTimeoutRef.current)
      }
    }
  }, [])

  // 渲染可见项目
  const visibleItems = useMemo(() => {
    const items_to_render = []
    
    for (let i = visibleRange.start; i <= visibleRange.end; i++) {
      if (i >= items.length) break
      
      const item = items[i]
      const top = itemPositions[i]
      const height = getItemHeight(i, item)
      const key = getItemKey(item, i)

      items_to_render.push(
        <div
          key={key}
          style={{
            position: 'absolute',
            top,
            left: 0,
            right: 0,
            height,
            transform: isScrolling ? 'translateZ(0)' : undefined // 硬件加速
          }}
        >
          {renderItem(item, i)}
        </div>
      )
    }

    return items_to_render
  }, [visibleRange, items, itemPositions, getItemHeight, getItemKey, renderItem, isScrolling])

  // 空状态
  if (items.length === 0 && !loading) {
    return (
      <div className={`flex items-center justify-center ${className}`} style={{ height, width }}>
        {emptyComponent || (
          <div className="text-center text-gray-500 dark:text-gray-400">
            <p>暂无数据</p>
          </div>
        )}
      </div>
    )
  }

  return (
    <div
      ref={containerRef}
      className={`overflow-auto ${className}`}
      style={{ height, width }}
      onScroll={handleScroll}
    >
      <div
        style={{
          height: totalHeight,
          position: 'relative'
        }}
      >
        {visibleItems}
        
        {/* 加载指示器 */}
        {loading && (
          <div
            style={{
              position: 'absolute',
              top: totalHeight,
              left: 0,
              right: 0,
              padding: '16px',
              display: 'flex',
              justifyContent: 'center'
            }}
          >
            {loadingComponent || <LoadingSpinner text="加载中..." />}
          </div>
        )}
      </div>
    </div>
  )
}

/**
 * 虚拟化网格组件
 */
export interface VirtualGridProps<T = any> {
  items: T[]
  itemWidth: number
  itemHeight: number
  renderItem: (item: T, index: number) => React.ReactNode
  height: number
  width?: number | string
  gap?: number
  overscan?: number
  onScroll?: (scrollTop: number) => void
  onEndReached?: () => void
  endReachedThreshold?: number
  loading?: boolean
  className?: string
  itemKey?: keyof T | ((item: T, index: number) => string | number)
}

export function VirtualGrid<T = any>({
  items,
  itemWidth,
  itemHeight,
  renderItem,
  height,
  width = '100%',
  gap = 0,
  overscan = 5,
  onScroll,
  onEndReached,
  endReachedThreshold = 200,
  loading = false,
  className = '',
  itemKey
}: VirtualGridProps<T>) {
  const containerRef = useRef<HTMLDivElement>(null)
  const [scrollTop, setScrollTop] = useState(0)
  const [containerWidth, setContainerWidth] = useState(0)

  // 监听容器宽度变化
  useEffect(() => {
    const updateWidth = () => {
      if (containerRef.current) {
        setContainerWidth(containerRef.current.clientWidth)
      }
    }

    updateWidth()
    window.addEventListener('resize', updateWidth)
    return () => window.removeEventListener('resize', updateWidth)
  }, [])

  // 计算列数
  const columnsCount = Math.floor((containerWidth + gap) / (itemWidth + gap)) || 1

  // 计算行数和总高度
  const rowsCount = Math.ceil(items.length / columnsCount)
  const totalHeight = rowsCount * (itemHeight + gap) - gap

  // 计算可见范围
  const visibleRange = useMemo(() => {
    const startRow = Math.floor(scrollTop / (itemHeight + gap))
    const endRow = Math.min(
      rowsCount - 1,
      Math.ceil((scrollTop + height) / (itemHeight + gap))
    )

    const start = Math.max(0, (startRow - overscan) * columnsCount)
    const end = Math.min(items.length - 1, (endRow + overscan + 1) * columnsCount - 1)

    return { start, end, startRow: Math.max(0, startRow - overscan) }
  }, [scrollTop, height, itemHeight, gap, rowsCount, columnsCount, overscan, items.length])

  // 生成项目键
  const getItemKey = useCallback((item: T, index: number): string | number => {
    if (typeof itemKey === 'function') {
      return itemKey(item, index)
    }
    if (typeof itemKey === 'string') {
      return (item as any)[itemKey]
    }
    return index
  }, [itemKey])

  // 处理滚动
  const handleScroll = useCallback((e: React.UIEvent<HTMLDivElement>) => {
    const scrollTop = e.currentTarget.scrollTop
    setScrollTop(scrollTop)
    onScroll?.(scrollTop)

    // 检查是否到达底部
    if (onEndReached) {
      const { scrollHeight, clientHeight } = e.currentTarget
      if (scrollHeight - scrollTop - clientHeight <= endReachedThreshold) {
        onEndReached()
      }
    }
  }, [onScroll, onEndReached, endReachedThreshold])

  // 渲染可见项目
  const visibleItems = useMemo(() => {
    const items_to_render = []
    
    for (let i = visibleRange.start; i <= visibleRange.end; i++) {
      if (i >= items.length) break
      
      const item = items[i]
      const row = Math.floor(i / columnsCount)
      const col = i % columnsCount
      const top = row * (itemHeight + gap)
      const left = col * (itemWidth + gap)
      const key = getItemKey(item, i)

      items_to_render.push(
        <div
          key={key}
          style={{
            position: 'absolute',
            top,
            left,
            width: itemWidth,
            height: itemHeight
          }}
        >
          {renderItem(item, i)}
        </div>
      )
    }

    return items_to_render
  }, [visibleRange, items, columnsCount, itemHeight, itemWidth, gap, getItemKey, renderItem])

  return (
    <div
      ref={containerRef}
      className={`overflow-auto ${className}`}
      style={{ height, width }}
      onScroll={handleScroll}
    >
      <div
        style={{
          height: totalHeight,
          position: 'relative'
        }}
      >
        {visibleItems}
        
        {/* 加载指示器 */}
        {loading && (
          <div
            style={{
              position: 'absolute',
              top: totalHeight,
              left: 0,
              right: 0,
              padding: '16px',
              display: 'flex',
              justifyContent: 'center'
            }}
          >
            <LoadingSpinner text="加载中..." />
          </div>
        )}
      </div>
    </div>
  )
}
